/*
 * smallptCPU.c
 *
 *  Created on: 10/giu/2013
 *      Author: sim1
 */


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>

#include "camera.h"
#include "scene.h"
#include "displayfunc.h"

int workGroupSize = 1;
Vec *colors,*colors2;
static unsigned int *seeds;
Camera camera, phantom;
static int currentSample = 0;
static int eyeSample=-1, lightSample=-1;
Sphere *spheres;
unsigned int sphereCount;
int ReInitCounter;

extern float maxdiff;
float maxdifflight=255, maxdiffeye=255;
float pila1[10], pila2[10];
extern flag;
extern int ccoo;
extern float ndep;
int i2=0;

void FreeBuffers() {
	free(seeds);
	free(colors);
	free(colors2);
	free(pixels);
	//free(lp);
}

void AllocateBuffers() {
	const int pixelCount = height * width ;
	int i;
	colors = malloc(sizeof(Vec[pixelCount*2]));
	colors2 = malloc(sizeof(Vec[pixelCount*2]));
	//lp = malloc(sizeof(LightPath)*500);

	seeds = malloc(sizeof(unsigned int[pixelCount * 2]));
	for (i = 0; i < pixelCount * 2; i++) {
		seeds[i] = rand();
		if (seeds[i] < 2)
			seeds[i] = 2;
	}

	counter = calloc(pixelCount*2,sizeof(unsigned int));
	pixels = malloc(sizeof(unsigned int[pixelCount*2]));
}

/* --UpdateRendering--
 * Main del rendering,
 * Scorre tutta l'immagine pixel per pixel
 * Lancia il raggio calcolandone la direzione
 * Richiama la funzione di Path Tracing
 * Converte il valore di radianza trovato in modo da poterlo mostrare nel modello RGB
 * */
void UpdateRendering(void) {
	double startTime = WallClockTime();

	const float invWidth = 1.f / width;
	const float invHeight = 1.f / height;

	printf("Passo %d\n",currentSample);
	printf("Path\n");
	int x, y;
	//Scorre l'immagine pixel per pixel
	for (y = 0; y < height; y++) { /* Loop over image rows */
        #pragma omp parallel for
		for (x = 0; x < width; x++) { /* Loop cols */
			const int i = (height - y - 1) * width + x; //indice del pixel
			const int i2 = 2 * i;
			const float r1 = GetRandom(&seeds[i2], &seeds[i2 + 1]) - .5f; //r1 e r2 sono valori random del tent filter
			const float r2 = GetRandom(&seeds[i2], &seeds[i2 + 1]) - .5f;
			const float kcx = (x + r1) * invWidth - .5f;
			const float kcy = (y + r2) * invHeight - .5f;

			//Calcolo della direzione del raggio in base a cx,cy e camera
			Vec rdir;
			vinit(rdir,
				camera.x.x * kcx + camera.y.x * kcy + camera.dir.x,
				camera.x.y * kcx + camera.y.y * kcy + camera.dir.y,
				camera.x.z * kcx + camera.y.z * kcy + camera.dir.z);

			Vec rorig;
			vsmul(rorig, 0.1f, rdir);
			vadd(rorig, rorig, camera.orig)

			vnorm(rdir);
			const Ray ray = {rorig, rdir};
			Vec r;

			RadiancePathTracing(spheres, sphereCount, &ray,
					&seeds[i2], &seeds[i2 + 1],&r);

			//RadianceDirectLighting(spheres, sphereCount, &ray,
			//		&seeds[i2], &seeds[i2+1], &r);
			if (currentSample == 0)
				colors[i] = r;
			else {
				const float k1 = currentSample;
				const float k2 = 1.f / (k1 + 1.f);
				//k1 e k2 dovrebbero essere una correzione di gamma
				colors[i].x = (colors[i].x * k1 + r.x) * k2;
				colors[i].y = (colors[i].y * k1 + r.y) * k2;
				colors[i].z = (colors[i].z * k1 + r.z) * k2;
			}

			//Si converte il valore di radianza calcolato per farlo rientrare nel range 0-255 del modello RGB
			pixels[y * width + x] = toInt(colors[i].x) |
					(toInt(colors[i].y) << 8) |
					(toInt(colors[i].z) << 16);
		}
	}

	const float elapsedTime = WallClockTime() - startTime;
	const float sampleSec = height * width / elapsedTime;
	sprintf(captionBuffer, "Rendering time %.3f sec (pass %d)  sample/sec  %.1fK\n",
			elapsedTime, currentSample, sampleSec / 1000.f);

	currentSample++;
	flag=1;
}

void UpdateRendering2(void) {

	double startTime = WallClockTime();
	int x, y, i,j,elementi_light;

	ccoo=0;
	ndep=0.;
	maxdiff=0;
	printf("Light\n");
	for (i = 0; i < sphereCount; i++) {
	const Sphere *light = &spheres[i];
		if (!viszero(light->e)) {
			/* It is a light source */

#pragma omp parallel for schedule(dynamic, 1) shared(maxdiff)
	   	    for (j=1;j <(LIGHT_POINTS/DEPTH) ;j++){
			/* Choose a point over the light source */
			Vec unitSpherePoint;
			UniformSampleSphere(GetRandom(&seeds[i2], &seeds[i2+1]), GetRandom(&seeds[i2], &seeds[i2+1]), &unitSpherePoint);
			Vec spherePoint;
			vsmul(spherePoint, light->rad, unitSpherePoint);
			vadd(spherePoint, spherePoint, light->p);

			/* Build the newray direction */
			Vec normal;
			vsub(normal, spherePoint, light->p);
			vnorm(normal);

			//vsmul(normal, 1.f, normal);

			float r1 = 2.f * FLOAT_PI * GetRandom(&seeds[i2], &seeds[i2+1]);
			float r2 = GetRandom(&seeds[i2], &seeds[i2+1]);
			float r2s = sqrt(r2);


			Vec u, a;

			if (fabs(normal.x) > .1f) {
				vinit(a, 0.f, 1.f, 0.f);
			} else {
				vinit(a, 1.f, 0.f, 0.f);
			}
			vxcross(u, a, normal);
			vnorm(u);

			Vec v;
			vxcross(v, normal, u);

			Vec newDir;
			vsmul(u, cos(r1) * r2s, u);
			vsmul(v, sin(r1) * r2s, v);
			vadd(newDir, u, v);
			vsmul(normal, sqrt(1 - r2), normal);
			vadd(newDir, newDir, normal);

			Ray currentRay;
			currentRay.o = spherePoint;
			currentRay.d = newDir;

			Vec r;
			//fprintf(stderr,"viszero p %d\n",viszero(light->e));
			RadianceLightTracing(spheres, sphereCount, &currentRay,
					&seeds[i2], &seeds[i2 + 1], &r, light, j );

			//fprintf(stderr,"viszero d %d\n",viszero(light->e));
		    }
			i2+=2;
			if(i2 > (height * width *2)) i2=0;
		}
	   }

	//for(elementi_light=0;elementi_light<10;elementi_light++){
		//printf("%d, x=%.3f y=%.3f z=%.3f\n ",elementi_light,lp[elementi_light].hp.x,lp[elementi_light].hp.y,lp[elementi_light].hp.z);
		//printf("rad %.3f %.3f %.3f\n",lp[elementi_light].rad.x,lp[elementi_light].rad.y,lp[elementi_light].rad.z);
	//}

	//const float elapsedTime = WallClockTime() - startTime;
	//const float sampleSec = height * width / elapsedTime;
	//sprintf(captionBuffer, "Rendering time %.3f sec (pass %d)  Sample/sec  %.1fK\n",
		//elapsedTime, eyeSample+lightSample , sampleSec / 1000.f);

	//currentSample++;
	lightSample++;

	flag =2;
	lightS=0;
	//printf("lightsample=%d eyesample=%d\n",lightSample,eyeSample);
	//flag = 2;
}

void ReInitScene() {
	currentSample = 0;
	FreeBuffers();
	AllocateBuffers();
	maxdifflight=255, maxdiffeye=255;
	eyeSample=-1, lightSample=-1;
	UpdateRendering2();
	printf("ReinitScene");
}

void ReInit(const int reallocBuffers) {
	// Check if I have to reallocate buffers
	printf("Sto chiamando reinit per la %d volta\n",ReInitCounter);
	fprintf(stderr,"ATTENZIONE\n");
	if (reallocBuffers) {
		FreeBuffers();
		AllocateBuffers();
	}
    ReInitCounter++;

	printf("ReinitCounter:%d\n",ReInitCounter);

	UpdateCamera();
	currentSample = 0;
	maxdifflight=255, maxdiffeye=255;
	eyeSample=-1, lightSample=-1;
	if(ReInitCounter%2==1)UpdateRendering2();
	printf("Ho finito il light e comincio il path\n");
	UpdateRendering();
}

int main(int argc, char *argv[]) {
	amiSmallptCPU = 1;

	fprintf(stderr, "Usage: %s\n", argv[0]);
	fprintf(stderr, "Usage: %s <window width> <window height> <scene file>\n", argv[0]);

	if (argc == 4) {
		width = atoi(argv[1]);
		height = atoi(argv[2]);
		ReadScene(argv[3]);
	} else if (argc == 1) {
		spheres = CornellSpheres;
		sphereCount = sizeof(CornellSpheres) / sizeof(Sphere);

		//vinit(camera.orig, 50.f, 45.f, 205.6f);
		//vinit(camera.orig, 50.f, 45.f, 155.6f);
		//vinit(camera.target, 50.f, 45.f, 204.6f);
		//vinit(camera.target, 50.f, 45.f - 0.042612f, 204.6);
		//vinit(camera.target, 50.f, 45.f - 0.042612f, 204.6);

		//vinit(camera.orig, 20.f, 35.f, 100.6f);
		//vinit(camera.target, 20.f, 25.f, 10.f);

		vinit(camera.orig, 50.f, 45.f, 205.6f);
		vinit(camera.target, 50.f, 45 - 0.042612f, 204.6);




	} else
		exit(-1);
	height+=1;
	width+=1;
	invWidth=14./width;
	invHeight=10.5/height;
	UpdateCamera();

	/*------------------------------------------------------------------------*/

	AllocateBuffers();

	/*------------------------------------------------------------------------*/

	InitGlut(argc, argv, "DR");

    glutMainLoop( );

	return 0;
}

